/*
 * Copyright (c) 2000-2005 by Hewlett-Packard Company.  All rights reserved.
 *
 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
 * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
 *
 * Permission is hereby granted to use or copy this program
 * for any purpose, provided the above notices are retained on all copies.
 * Permission to modify the code and to distribute modified code is granted,
 * provided the above notices are retained, and a notice that the code was
 * modified is included with the above copyright notice.
 */

/* Included indirectly from a thread-library-specific file.     */
/* This is the interface for thread-local allocation, whose     */
/* implementation is mostly thread-library-independent.         */
/* Here we describe only the interface that needs to be known   */
/* and invoked from the thread support layer; the actual        */
/* implementation also exports GC_malloc and friends, which     */
/* are declared in gc.h.                                        */

#ifndef GC_THREAD_LOCAL_ALLOC_H
#define GC_THREAD_LOCAL_ALLOC_H

#include "gc_priv.h"

#ifdef THREAD_LOCAL_ALLOC

#  if defined(USE_HPUX_TLS)
#    error USE_HPUX_TLS macro was replaced by USE_COMPILER_TLS
#  endif

#  include <stdlib.h>

EXTERN_C_BEGIN

#  if !defined(USE_PTHREAD_SPECIFIC) && !defined(USE_WIN32_SPECIFIC)    \
      && !defined(USE_WIN32_COMPILER_TLS) && !defined(USE_COMPILER_TLS) \
      && !defined(USE_CUSTOM_SPECIFIC)
#    if defined(GC_WIN32_THREADS)
#      if defined(CYGWIN32) && GC_GNUC_PREREQ(4, 0)
#        if defined(__clang__)
/* As of Cygwin clang3.5.2, thread-local storage is unsupported.    */
#          define USE_PTHREAD_SPECIFIC
#        else
#          define USE_COMPILER_TLS
#        endif
#      elif defined(__GNUC__) || defined(MSWINCE)
#        define USE_WIN32_SPECIFIC
#      else
#        define USE_WIN32_COMPILER_TLS
#      endif /* !GNU */

#    elif defined(HOST_ANDROID)
#      if defined(ARM32) \
          && (GC_GNUC_PREREQ(4, 6) || GC_CLANG_PREREQ_FULL(3, 8, 256229))
#        define USE_COMPILER_TLS
#      elif !defined(__clang__) && !defined(ARM32)
/* TODO: Support clang/arm64 */
#        define USE_COMPILER_TLS
#      else
#        define USE_PTHREAD_SPECIFIC
#      endif

#    elif defined(LINUX) && GC_GNUC_PREREQ(3, 3) /* && !HOST_ANDROID */
#      if defined(ARM32) || defined(AVR32)
/* TODO: change to USE_COMPILER_TLS on Linux/arm */
#        define USE_PTHREAD_SPECIFIC
#      elif defined(AARCH64) && defined(__clang__) && !GC_CLANG_PREREQ(8, 0)
/* To avoid "R_AARCH64_ABS64 used with TLS symbol" linker warnings. */
#        define USE_PTHREAD_SPECIFIC
#      else
#        define USE_COMPILER_TLS
#      endif

#    elif (defined(COSMO) || defined(FREEBSD)                                 \
           || (defined(NETBSD) && __NetBSD_Version__ >= 600000000 /* 6.0 */)) \
        && (GC_GNUC_PREREQ(4, 4) || GC_CLANG_PREREQ(3, 9))
#      define USE_COMPILER_TLS

#    elif defined(HPUX)
#      ifdef __GNUC__
/* Empirically, as of gcc 3.3, USE_COMPILER_TLS doesn't work. */
#        define USE_PTHREAD_SPECIFIC
#      else
#        define USE_COMPILER_TLS
#      endif

#    elif defined(IRIX5) || defined(OPENBSD) || defined(SOLARIS) \
        || defined(NN_PLATFORM_CTR) || defined(NN_BUILD_TARGET_PLATFORM_NX)
#      define USE_CUSTOM_SPECIFIC /* Use our own. */

#    else
#      define USE_PTHREAD_SPECIFIC
#    endif
#  endif /* !USE_x_SPECIFIC */

#  ifndef THREAD_FREELISTS_KINDS
#    ifdef ENABLE_DISCLAIM
#      define THREAD_FREELISTS_KINDS (NORMAL + 2)
#    else
#      define THREAD_FREELISTS_KINDS (NORMAL + 1)
#    endif
#  endif /* !THREAD_FREELISTS_KINDS */

/* The first GC_TINY_FREELISTS free lists correspond to the first   */
/* GC_TINY_FREELISTS multiples of GC_GRANULE_BYTES, i.e. we keep    */
/* separate free lists for each multiple of GC_GRANULE_BYTES up to  */
/* (GC_TINY_FREELISTS-1) * GC_GRANULE_BYTES.  After that they may   */
/* be spread out further.                                           */

/* This should be used for the tlfs field in the structure pointed  */
/* to by a GC_thread.  Free lists contain either a pointer or       */
/* a small count reflecting the number of granules allocated at     */
/* that size:                                                       */
/* - 0 means thread-local allocation in use, free list empty;       */
/* - >0 but <=DIRECT_GRANULES means using global allocation, too    */
/*   few objects of this size have been allocated by this thread;   */
/* - >DIRECT_GRANULES but <HBLKSIZE means transition to local       */
/* allocation, equivalent to 0;                                     */
/* - >=HBLKSIZE means pointer to nonempty free list.                */

struct thread_local_freelists {
  /* Note: Preserve *_freelists names for some clients. */
  void *_freelists[THREAD_FREELISTS_KINDS][GC_TINY_FREELISTS];
#  define ptrfree_freelists _freelists[PTRFREE]
#  define normal_freelists _freelists[NORMAL]
#  ifdef GC_GCJ_SUPPORT
  void *gcj_freelists[GC_TINY_FREELISTS];
  /* A value used for gcj_freelists[-1]; allocation is erroneous. */
#    define ERROR_FL GC_WORD_MAX
#  endif

  /* Do not use local free lists for up to this much allocation.    */
#  define DIRECT_GRANULES (HBLKSIZE / GC_GRANULE_BYTES)
};
typedef struct thread_local_freelists *GC_tlfs;

#  if defined(USE_PTHREAD_SPECIFIC)
#    define GC_getspecific pthread_getspecific
#    define GC_setspecific pthread_setspecific
#    define GC_key_create pthread_key_create
/* Explicitly delete the value to stop the TLS destructor from    */
/* being called repeatedly.                                       */
#    define GC_remove_specific(key) (void)pthread_setspecific(key, NULL)
#    define GC_remove_specific_after_fork(key, t) \
      (void)0 /* no action needed */
typedef pthread_key_t GC_key_t;
#  elif defined(USE_COMPILER_TLS) || defined(USE_WIN32_COMPILER_TLS)
#    define GC_getspecific(x) (x)
#    define GC_setspecific(key, v) ((key) = (v), 0)
#    define GC_key_create(key, d) 0
/* Just to clear the pointer to tlfs. */
#    define GC_remove_specific(key) (void)GC_setspecific(key, NULL)
#    define GC_remove_specific_after_fork(key, t) (void)0
typedef void *GC_key_t;
#  elif defined(USE_WIN32_SPECIFIC)
#    define GC_getspecific TlsGetValue
/* Note: we assume that zero means success, msft does the opposite.   */
#    define GC_setspecific(key, v) !TlsSetValue(key, v)
#    ifndef TLS_OUT_OF_INDEXES
/* This is currently missing in WinCE.      */
#      define TLS_OUT_OF_INDEXES (DWORD)0xFFFFFFFF
#    endif
#    define GC_key_create(key, d) \
      ((d) != 0 || (*(key) = TlsAlloc()) == TLS_OUT_OF_INDEXES ? -1 : 0)
/* TODO: Is TlsFree needed on process exit/detach? */
#    define GC_remove_specific(key) (void)GC_setspecific(key, NULL)
#    define GC_remove_specific_after_fork(key, t) (void)0
typedef DWORD GC_key_t;
#  elif defined(USE_CUSTOM_SPECIFIC)
EXTERN_C_END
#    include "specific.h"
EXTERN_C_BEGIN
#  else
#    error implement me
#  endif

/* Each thread structure must be initialized.  This call must be    */
/* made from the new thread.  Caller should hold the allocator      */
/* lock.                                                            */
GC_INNER void GC_init_thread_local(GC_tlfs p);

/* Called when a thread is unregistered, or exits.  Caller should   */
/* hold the allocator lock.                                         */
GC_INNER void GC_destroy_thread_local(GC_tlfs p);

/* The thread support layer must arrange to mark thread-local free  */
/* lists explicitly, since the link field is often invisible to the */
/* marker.  It knows how to find all threads; we take care of an    */
/* individual thread free-list structure.                           */
GC_INNER void GC_mark_thread_local_fls_for(GC_tlfs p);

#  ifdef GC_ASSERTIONS
GC_bool GC_is_thread_tsd_valid(void *tsd);
void GC_check_tls_for(GC_tlfs p);
#    if defined(USE_CUSTOM_SPECIFIC)
void GC_check_tsd_marks(tsd *key);
#    endif
#  endif /* GC_ASSERTIONS */

#  ifndef GC_ATTR_TLS_FAST
#    define GC_ATTR_TLS_FAST /* empty */
#  endif

/* This is set up by the thread_local_alloc implementation.         */
/* No need for cleanup on thread exit.  But the thread support      */
/* layer makes sure that GC_thread_key is traced, if necessary.     */
extern
#  if defined(USE_COMPILER_TLS)
    __thread GC_ATTR_TLS_FAST
#  elif defined(USE_WIN32_COMPILER_TLS)
    __declspec(thread) GC_ATTR_TLS_FAST
#  endif
        GC_key_t GC_thread_key;

EXTERN_C_END

#endif /* THREAD_LOCAL_ALLOC */

#endif /* GC_THREAD_LOCAL_ALLOC_H */
